package com.katesoft.scale4j.agent;

import com.katesoft.scale4j.agent.args.AgentOptions;
import com.katesoft.scale4j.agent.jmx.ServiceManagerJMXExporter;
import com.katesoft.xml.jvmcluster.ApplicationConfigurationDocument.ApplicationConfiguration;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;

import java.util.concurrent.Callable;

import static com.katesoft.scale4j.common.lang.RuntimeUtility.isJunit;
import static com.katesoft.scale4j.log.LogFactory.getLogger;

/** @author kate2007 */
public class AgentLauncher implements Callable<ServiceLauncher>, DisposableBean
{
    private AgentOptions options;
    private Thread launchThread;
    private Throwable exception;
    private ServiceLauncher launcher;
    private ClassPathXmlApplicationContext context;

    public AgentLauncher(AgentOptions options)
    {
        this.options = options;
    }

    @Override
    public ServiceLauncher call() throws Exception
    {
        context = new ClassPathXmlApplicationContext("META-INF/spring/jvmcluster$agent.xml");
        //
        ServiceManagerJMXExporter exporter = (ServiceManagerJMXExporter) context.getBean("jvmcluster.agent.service.manager.jmx.exporter");
        ApplicationConfigurationReader reader = (ApplicationConfigurationReader) context.getBean("jvmcluster.appConfiguration.reader");
        //
        ApplicationConfiguration configuration =
            (options.appConfigurationLocation() != null ? reader.parseConfiguration(options.appConfigurationLocation().getInputStream()) :
                reader.parseConfiguration(new ClassPathResource(Agent.DEFAULT_LOCATION).getInputStream()));
        //start services
        launcher = new ServiceLauncher(configuration, options);
        exporter.setLauncher(launcher);
        exporter.setContext(context);
        Thread hook = new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                getLogger(Agent.class).info("JVM shutdown hook invoked to stop all services");
                try {
                    launcher.stopAllServices(false);
                }
                finally {
                    getLogger(Agent.class).info("destroying application context = %s", context.getDisplayName());
                    context.destroy();
                }
            }
        });
        hook.setName("scale4j.agent.shutdown.hook.thread");
        Runtime.getRuntime().addShutdownHook(hook);
        launchThread = new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                try {
                    launcher.start();
                }
                catch (Throwable e) {
                    exception = e;
                    getLogger(AgentLauncher.class).error(e);
                    if (!isJunit()) {
                        System.exit(-1);
                    }
                }
            }
        });
        launchThread.setName("scale4j.agent.launch.thread");
        launchThread.start();
        return launcher;
    }

    public void waitFor() throws Throwable
    {
        launchThread.join();
        if (exception != null) { throw exception; }
    }

    @Override
    public void destroy() throws Exception
    {
        try {
            if (context != null) { context.destroy(); }
        }
        finally {
            if (launcher != null) { launcher.stopAllServices(true); }
        }
    }
}
